package com.xsystem.api;

import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.xsystem.api.exception.RequiredParamsMissingException;
import com.xsystem.api.util.ApiMonitor;
import com.xsystem.api.util.ApiResponseHandler;
import com.xsystem.api.util.MessagesUtil;
import com.xsystem.orm.user.User;
import com.xsystem.util.StringUtil;

public abstract class Api extends ApiResponseHandler {

	protected static final String DEFAULT_LANGUAGE_CODE = "en";

	public static final String IN_USERNAME = "username";
	public static final String IN_PASSWORD = "password";

	public static final String IN_IPADDRESS = "ipAddress";
	public static final String IN_UPLOADS = "uploads";

	public static final String OUT_STATUS = "status";
	public static final String OUT_ERROR_CODE = "errorCode";
	public static final String OUT_MESSAGE = "errorMsg";

	public static final String API_IP_ADDRESS = "127.0.0.1";

	private static final String CLASS_CORE = "com.xsystem.api.publicapi.core";
	private static final String CLASS_SUPPORT = "com.xsystem.api.publicapi.support";
	private static final String CLASS_PRIVATE = "com.xsystem.api.privateapi";

	private static final Logger LOG_CORE = LoggerFactory.getLogger(CLASS_CORE);
	private static final Logger LOG_SUPPORT = LoggerFactory
			.getLogger(CLASS_SUPPORT);
	private static final Logger LOG_PRIVATE = LoggerFactory
			.getLogger(CLASS_PRIVATE);
	private static final Logger LOG_API = LoggerFactory
			.getLogger("xSystemLogger");

	protected ApiMonitor apiMonitor;

	public enum Status {
		OK("OK"), FAILED("fail"), WARNING("warning");

		private String name;

		private Status(String name) {
			this.name = name;
		}

		@Override
		public String toString() {
			return name;
		}
	}

	public static class BulkOperationOutput {
		private int numModified = 0;
		private int numToModify = 0;
		private Map<String, Object> output = new HashMap<String, Object>();

		public BulkOperationOutput(int numToModify, int numModified,
				Map<String, Object> output) {
			super();
			this.numModified = numModified;
			this.numToModify = numToModify;
			this.output = output;
		}

		public Api.Status getOperationStatus() {
			if (numToModify == 0 || numModified == 0) {
				return Api.Status.FAILED;
			} else if (numModified == numToModify) {
				return Api.Status.OK;
			} else {
				return Api.Status.WARNING;
			}
		}

		public Map<String, Object> getOutput() {
			return output;
		}
	}

	

	public Map<String, Object> execute(Map<String, String> parameters) {
		try {
			if (apiMonitor != null) {
				apiMonitor.setDeveloperLogger(getLogger());
			}
			return this.executeApi(parameters);
		} catch (RequiredParamsMissingException ex) {
			return prepareCustomErrorResponse(XController.ERROR_PARAMS_MISSING,
					MessagesUtil.getProperty(Api.class,
							XController.ERROR_PARAMS_MISSING, null,
							ex.getMessage()));
		}
	}

	private Logger getLogger() {
		String className = this.getClass().getName();
		if (className.contains(CLASS_CORE)) {
			return LOG_CORE;
		} else if (className.contains(CLASS_PRIVATE)) {
			return LOG_PRIVATE;
		} else if (className.contains(CLASS_SUPPORT)) {
			return LOG_SUPPORT;
		} else {
			return LOG_API;
		}
	}

	protected void logUser(User user) {
		if (apiMonitor != null) {
			apiMonitor.setUser(user);
		}
	}

	protected abstract Map<String, Object> executeApi(
			Map<String, String> parameters)
			throws RequiredParamsMissingException;

	protected Map<String, Object> prepareErrorResponseWithParams(
			Map<String, Object> responseMap, String msg) {
		return MessagesUtil.prepareErrorResponseWithParams(responseMap, msg);
	}
	
	/**
	 * Utility methods
	 * @return
	 */
	protected String getRequiredParam(Map<String, String> parameters,
			String paramName) throws RequiredParamsMissingException {
		String paramValue = parameters.get(paramName);
		if (paramValue == null || paramValue.trim().isEmpty()) {
			throw new RequiredParamsMissingException(paramName);
		}
		return paramValue;
	}

	protected String getOptionalParam(Map<String, String> parameters,
			String paramName) {
		return parameters.get(paramName);
	}

	protected String getOptionalParam(Map<String, String> parameters,
			String paramName, String defaultValue) {
		String val = parameters.get(paramName);
		if (StringUtil.isEmpty(val)) {
			return defaultValue;
		} else {
			return val;
		}
	}

	public ApiMonitor getApiMonitor() {
		return apiMonitor;
	}

	public void setApiMonitor(ApiMonitor apiMonitor) {
		this.apiMonitor = apiMonitor;
	}

}
